/**************************************************
 * 上海美峰数码科技有限公司(http://www.morefuntek.com)
 * 模块名称: Object2Excel
 * 功能描述：
 * 文件名：Object2Excel.java
 **************************************************
 */
package com.annotation.test;

import java.io.File;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jxl.Workbook;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;

/**
 * 功能描述
 * 
 * @author yaowenhao
 * @since 2011-4-26
 * @version 1.0
 */
public class Object2Excel<E> {

	/** 存放列名与字段名的对应关系及@Head注解信息 */
	private List<XlskeyValue> cTitle = null;

	/**
	 * 根据@Hearder注解提取对象信息构建列名与字段名的对应
	 * 
	 * @param obj
	 *            需要解析的对象
	 */
	private void parseObject(E obj) {
		List<XlskeyValue> aTitle = new ArrayList<XlskeyValue>(); // 存放注解字段
		List<XlskeyValue> title = new ArrayList<XlskeyValue>(); // 存放没有注解的字段
		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field field : fields) {
			boolean flag = field.isAnnotationPresent(Header.class);
			field.setAccessible(true);
			String fName = field.getName();
			XlskeyValue kv = new XlskeyValue();
			kv.setfName(fName);
			if (flag) { // 添加带注解的字段
				Header header = field.getAnnotation(Header.class);
				kv.setHeader(header);
				kv.setcName(header.value());
				aTitle.add(kv);
			} else {
				kv.setcName(fName);
				kv.setHeader(null);
				title.add(kv);
			}
		}
		// 若没有任何@Header注解字段
		if (aTitle.isEmpty()) {
			cTitle = title;
		} else {
			// TODO 以后可增加按Annotation信息改变列顺序等,目前按字段顺序导出
			cTitle = aTitle;
		}
	}

	/**
	 * 将任意对象的字段和值转换为Map<fieldName,fieldValue>形式,并返回该Map
	 * 
	 * @param obj
	 *            需要转换的对象
	 * @return 该类对象字段有@Header注解的字段,否则返回全部字段.
	 * @throws Exception
	 */
	private Map<String, Object> ObjectField2Map(E obj) throws Exception {
		Map<String, Object> aMap = new HashMap<String, Object>(); // 存放解析后的注解字段
		Map<String, Object> map = new HashMap<String, Object>(); // 存放解析后的非注解字段
		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field field : fields) {
			field.setAccessible(true);
			String fName = field.getName();
			Object value = field.get(obj);
			boolean flag = field.isAnnotationPresent(Header.class);
			if (flag) {
				aMap.put(fName, value);
			} else { // 无注解字段
				map.put(fName, value);
			}
		}
		if (aMap.isEmpty()) {
			return map;
		}
		return aMap;
	}

	/**
	 * 将一个list对象导出成一个Excel
	 * 
	 * @param srcs
	 *            待导出的原数据
	 * @param filename
	 *            导出的文件名
	 * @return 导出的文件
	 * @throws Exception
	 */
	public File Export(List<E> srcs, String filename) throws Exception {
		File excel = new File(filename);
		WritableWorkbook workbook = Workbook.createWorkbook(excel);
		WritableSheet sheet = workbook.createSheet("name", 0);
		if (srcs.size() > 0) {
			parseObject(srcs.get(0));
		}
		fillTitle(sheet);
		for (E obj : srcs) {
			fillCell(ObjectField2Map(obj), sheet);
		}
		workbook.write();
		workbook.close();
		return excel;
	}

	/**
	 * 功能描述
	 * 
	 * @param sheet
	 * @throws Exception
	 * @throws
	 */
	private void fillTitle(WritableSheet sheet) throws Exception {
		for (int col = 0; col < cTitle.size(); col++) {
			XlskeyValue xkv = cTitle.get(col);
			jxl.write.Label label = new jxl.write.Label(col, 0, xkv.cName);
			sheet.addCell(label);
		}
	}

	/**
	 * 见解析出的字段Map填充到单元格中
	 * 
	 * @param Mfield
	 *            字段Map
	 * @param sheet
	 *            待填充的sheet
	 * @throws Exception
	 */
	private void fillCell(Map<String, Object> Mfield, WritableSheet sheet)
			throws Exception {
		int row = sheet.getRows();
		for (int col = 0; col < cTitle.size(); col++) {
			String key = cTitle.get(col).fName;
			Object value = Mfield.get(key);
			String cellValue = "";
			if (value != null) {
				cellValue = value.toString(); // TODO 以后需要根据@Header注解信息加强处理
			}
			jxl.write.Label label = new jxl.write.Label(col, row, cellValue);
			sheet.addCell(label);
		}

	}

	private WritableSheet createSheet(WritableWorkbook workbook, String name) {
		int number = workbook.getNumberOfSheets();
		WritableSheet sheet = workbook.createSheet(name, number);
		return sheet;
	}

	public OutputStream Export(List<E> srcs) {
		return null;
	}

	/**
	 * 用来存放Excel中列名与字段名的对应关系及@Head注解信息的内部类
	 * 
	 * @author yaowenhao
	 * @since 2011-4-28
	 * @version 1.0
	 */
	class XlskeyValue {
		String cName;
		String fName;
		Header header;

		public String getcName() {
			return cName;
		}

		public void setcName(String cName) {
			this.cName = cName;
		}

		public String getfName() {
			return fName;
		}

		public void setfName(String fName) {
			this.fName = fName;
		}

		public Header getHeader() {
			return header;
		}

		public void setHeader(Header header) {
			this.header = header;
		}
	}
}
